<!--
 Copyright (c) 2016,2017,2018,2019,2020,2021,2022 Klaus Landsdorf (http://node-red.plus/)
 Copyright 2016 - Jason D. Harper, Argonne National Laboratory
 Copyright 2015,2016 - Mika Karaila, Valmet Automation Inc.
 All rights reserved.
 node-red-contrib-modbus

 @author <a href="mailto:klaus.landsdorf@bianco-royal.de">Klaus Landsdorf</a> (Bianco Royal)
-->
<script type="text/javascript">
  RED.nodes.registerType('modbus-client', {
    category: 'config',
    defaults: {

      name: {value: ''},
      clienttype: {value: 'tcp', required: true},
      bufferCommands: {value: true},
      stateLogEnabled: {value: false},
      queueLogEnabled: {value: false},
      failureLogEnabled: {value: true},

      tcpHost: {value: '127.0.0.1', required: true},
      tcpPort: {value: 502, required: true, validate: RED.validators.number()},
      tcpType: {value: 'DEFAULT', required: true},

      serialPort: {value: '/dev/ttyUSB', required: true},
      serialType: {value: 'RTU-BUFFERD', required: true},
      serialBaudrate: {value: 9600, required: true, validate: RED.validators.number()},
      serialDatabits: {value: 8, required: true, validate: RED.validators.number()},
      serialStopbits: {value: 1, required: true, validate: RED.validators.number()},
      serialParity: {value: 'none', required: true},
      serialConnectionDelay: {value: 100},
      serialAsciiResponseStartDelimiter: {value: '0x3A'},

      unit_id: {value: 1},
      commandDelay: {value: 1},
      clientTimeout: {value: 1000},
      reconnectOnTimeout: {value: true},
      reconnectTimeout: {value: 2000},
      parallelUnitIdsAllowed: {value: true},

      showErrors: {value: false},
      showWarnings: {value: true},
      showLogs: {value: true}
    },
    label: function () {
      let node = this
      if (node.clienttype == 'tcp') {
        return node.name || 'modbus-tcp@' + node.tcpHost + ':' + node.tcpPort
      } else {
        return node.name || 'modbus-serial@' + node.serialPort + ':' + node.serialBaudrate
      }
    },
    oneditprepare: function () {
      let previous = null
      let node = this

      let tabs = RED.tabs.create({
        id: "node-input-modbus-tabs",
        onchange: function (tab) {
          $("#node-input-tabs-content").children().hide()
          $("#" + tab.id).show()
        }
      })

      tabs.addTab({
        id: "modbus-settings-tab",
        label: this._("modbus-contrib.tabs-label.settings")
      })

      tabs.addTab({
        id: "modbus-queues-tab",
        label: this._("modbus-contrib.tabs-label.queues")
      })

      tabs.addTab({
        id: "modbus-options-tab",
        label: this._("modbus-contrib.tabs-label.options")
      })

      node.ports = []
      let clientTypeSelector = $('#node-config-input-clienttype')
      let serialTypeSelector = $('#node-config-input-serialType')
      let inputsSerial = $('#node-inputs-modbus-serial')
      let inputsSerialExtras = $('#node-inputs-modbus-serial-extras')
      let inputsAsciiExtras = $('#node-inputs-ascii-extras')
      let inputsTCP = $('#node-inputs-modbus-tcp')

      clientTypeSelector.on('focus', function () {
        previous = this.value
      }).change(function () {

        if (previous == null) {
          previous = $('#node-config-input-clienttype').val()
        }

        if (node.unit_id) {
          node.unit_id = parseInt(node.unit_id)
        }

        if (node.commandDelay) {
          node.commandDelay = parseInt(node.commandDelay)
        }

        if (node.clientTimeout) {
          node.clientTimeout = parseInt(node.clientTimeout)
        }

        if (node.reconnectTimeout) {
          node.reconnectTimeout = parseInt(node.reconnectTimeout)
        }

        switch (clientTypeSelector.val()) {
          case 'tcp':
            inputsSerialExtras.hide()
            inputsSerial.hide()
            inputsTCP.show()

            if (node.tcpHost) {
              node.tcpHost.required = true
              node.tcpPort.required = true
              if (node.tcpType) {
                node.tcpType.required = true
              }
            }

            if (node.serialPort) {
              node.serialPort.required = false
              if (node.serialType) {
                node.serialType.required = false
              }
              node.serialBaudrate.required = false
            }

            if (node.serialDatabits) {
              node.serialDatabits.required = false
              node.serialStopbits.required = false
              node.serialParity.required = false
            }
            break
          case 'serial':
          case 'simpleser':
            if (clientTypeSelector.val() === 'simpleser') {
              inputsSerialExtras.hide()
              node.serialDatabits.value = 8
              node.serialStopbits.value = 1
              node.serialParity.value = 'none'
            } else {
              inputsSerialExtras.show()
            }

            inputsSerial.show()
            inputsTCP.hide()

            if (node.tcpHost) {
              node.tcpHost.required = false
              node.tcpPort.required = false
              if (node.tcpType) {
                node.tcpType.required = false
              }
            }

            if (node.serialPort) {
              node.serialPort.required = true
              if (node.serialType) {
                node.serialType.required = true
              }
              node.serialBaudrate.required = true
            }

            if (node.serialDatabits) {
              node.serialDatabits.required = true
              node.serialStopbits.required = true
              node.serialParity.required = true
            }
            break
          default:
            break
        }
      })

      serialTypeSelector.on('focus').change(function () {
        if (serialTypeSelector.val() === 'ASCII') {
          inputsAsciiExtras.show()

          if (node.serialAsciiResponseStartDelimiter) {
            node.serialAsciiResponseStartDelimiter = node.serialAsciiResponseStartDelimiter
          }
        } else {
          inputsAsciiExtras.hide()
        }
      })

      try {
        $('#node-config-input-serialPort').autocomplete('destroy')
      }
      catch (err) {
      }

      $('#node-config-lookup-serial').click(function () {
        $('#node-config-lookup-serial').addClass('disabled')

        $.getJSON('modbus/serial/ports', function (data) {
          $('#node-config-lookup-serial').removeClass('disabled')
          node.ports = []

          $.each(data, function (i, port) {
            node.ports.push(port.path)
          })

          $('#node-config-input-serialPort').autocomplete({
            source: node.ports,
            minLength: 0,
            close: function (event, ui) {
              $('#node-config-input-serialPort').autocomplete('destroy')
            }
          }).autocomplete('search', '')
        })
      })
    }
  })
</script>

<script type="text/x-red" data-template-name="modbus-client">
    <div class="form-row">
        <ul style="background: #fff; min-width: 600px; margin-bottom: 20px;" id="node-input-modbus-tabs"></ul>
    </div>
    <div id="node-input-tabs-content" style="min-height: 170px;">
        <div id="modbus-settings-tab" style="display:none">
            <div class="form-row">
                <label for="node-config-input-name"><i class="icon-tag"></i> <span data-i18n="node-red:common.label.name"></span></label>
                <input type="text" id="node-config-input-name" placeholder="Name">
            </div>
            <div class="form-row">
                <label for="node-config-input-clienttype"><i class="icon-bookmark"></i> <span data-i18n="modbus-contrib.label.type"></span></label>
                <select type="text" id="node-config-input-clienttype" style="width:140px;">
                    <option value="tcp" select>TCP</option>
                    <option value="simpleser">Serial</option>
                    <option value="serial">Serial Expert</option>
                </select>
            </div>
            <hr>
            <div id="node-inputs-modbus-tcp">
                <div class="form-row">
                    <label for="node-config-input-tcpHost"><i class="icon-bookmark"></i> <span data-i18n="modbus-contrib.label.host"></span></label>
                    <input type="text" id="node-config-input-tcpHost">
                </div>
                <div class="form-row">
                    <label for="node-config-input-tcpPort"><i class="icon-bookmark"></i> <span data-i18n="modbus-contrib.label.port"></span></label>
                    <input type="text" id="node-config-input-tcpPort" placeholder="502">
                </div>
                <div class="form-row">
                    <label for="node-config-input-tcpType"><i class="icon-bookmark"></i> <span data-i18n="modbus-contrib.label.tcpType"></span></label>
                    <select type="text" id="node-config-input-tcpType" style="width:140px;">
                        <option value="DEFAULT" select>DEFAULT</option>
                        <option value="TCP-RTU-BUFFERED">RTU-BUFFERED</option>
                        <option value="TELNET">TELNET</option>
                        <option value="C701">C701</option>
                    </select>
                </div>
            </div>
            <div id="node-inputs-modbus-serial">
                <div class="form-row">
                    <label for="node-config-input-serialPort"><i class="fa fa-random"></i> <span data-i18n="modbus-contrib.label.serialport"></span></label>
                    <input type="text" id="node-config-input-serialPort" style="width:60%;" placeholder="/dev/ttyUSB or COM4">
                    <a id="node-config-lookup-serial" class="red-ui-button"><i id="node-config-lookup-serial-icon" class="fa fa-search"></i></a>
                </div>
                <div class="form-row">
                    <label for="node-config-input-serialType"><i class="icon-bookmark"></i> <span data-i18n="modbus-contrib.label.serialType"></span></label>
                    <select type="text" id="node-config-input-serialType" style="width:140px;">
                        <option value="RTU-BUFFERD" select>RTU-BUFFERD</option>
                        <option value="RTU">RTU</option>
                        <option value="ASCII">ASCII</option>
                    </select>
                </div>
                <div class="form-row">
                    <label for="node-config-input-serialBaudrate"><i class="icon-bookmark"></i> <span data-i18n="modbus-contrib.label.baudRate"></span></label>
                    <select id="node-config-input-serialBaudrate" style="max-width:140px">
                        <option value="115200">115200</option>
                        <option value="57600">57600</option>
                        <option value="38400">38400</option>
                        <option value="19200">19200</option>
                        <option value="9600">9600</option>
                        <option value="4800">4800</option>
                        <option value="2400">2400</option>
                        <option value="1200">1200</option>
                        <option value="300">300</option>
                        <option value="110">110</option>
                        <option value="75">75</option>
                    </select>
                </div>
            </div>
            <div id="node-inputs-modbus-serial-extras">
                <div class="form-row">
                    <label for="node-config-input-serialDatabits"><i class="icon-bookmark"></i> <span data-i18n="modbus-contrib.label.dataBits"></span></label>
                    <select id="node-config-input-serialDatabits" style="max-width:80px">
                        <option value="8">8</option>
                        <option value="7">7</option>
                        <option value="6">6</option>
                        <option value="5">5</option>
                    </select>
                </div>
                <div class="form-row">
                    <label for="node-config-input-serialStopbits"><i class="icon-bookmark"></i> <span data-i18n="modbus-contrib.label.stopBits"></span></label>
                    <select id="node-config-input-serialStopbits" style="max-width:80px">
                        <option value="1">1</option>
                        <option value="1.5">1.5</option>
                        <option value="2">2</option>
                    </select>
                </div>
                <div class="form-row">
                    <label for="node-config-input-serialParity"><i class="icon-bookmark"></i> <span data-i18n="modbus-contrib.label.parity"></span></label>
                    <select id="node-config-input-serialParity" style="max-width:80px">
                        <option value="none">None</option>
                        <option value="even">Even</option>
                        <option value="mark">Mark</option>
                        <option value="odd">Odd</option>
                        <option value="space">Space</option>
                    </select>
                </div>
                <div class="form-row">
                    <label for="node-config-input-serialConnectionDelay">
                        <i class="fa fa-random"></i>
                        <span data-i18n="modbus-contrib.label.serialconnectiondelay"></span>
                    </label>
                    <input type="text" id="node-config-input-serialConnectionDelay" style="max-width:80px" placeholder="500">
                </div>
            </div>
          <div id="node-inputs-ascii-extras">
            <div class="form-row">
              <label for="node-config-input-serialAsciiResponseStartDelimiter"><i class="icon-bookmark"></i> <span data-i18n="modbus-contrib.label.serialAsciiResponseStartDelimiter"></span></label>
              <input type="text" id="node-config-input-serialAsciiResponseStartDelimiter" style="max-width:80px" placeholder="0x3A (:)">
            </div>
          </div>
            <hr>
            <div class="form-row">
                <label for="node-config-input-unit_id"><i class="icon-bookmark"></i> <span data-i18n="modbus-contrib.label.unitId"></span></label>
                <input type="text" id="node-config-input-unit_id" style="max-width:80px">
            </div>
            <div class="form-row">
                <label for="node-config-input-clientTimeout"><i class="icon-time"></i> <span data-i18n="modbus-contrib.label.timeout"></span></label>
                <input type="text" id="node-config-input-clientTimeout" placeholder="1000" style="max-width:80px">
            </div>
            <div class="form-row">
                <label style="min-width:190px" for="node-config-input-reconnectOnTimeout"><i class="fa fa-th"></i> <span
                data-i18n="modbus-contrib.label.reconnectOnTimeout"></span></label>
                <input type="checkbox" id="node-config-input-reconnectOnTimeout" style="max-width:30px">
            </div>
            <div class="form-row">
                <label for="node-config-input-reconnectTimeout">
                    <i class="icon-time"></i>
                    <span data-i18n="modbus-contrib.label.reconnectTimeout"></span>
                </label>
                <input type="text" id="node-config-input-reconnectTimeout" placeholder="2000" style="max-width:80px">
            </div>
        </div>
        <div id="modbus-queues-tab" style="display:none">
            <div class="form-row">
                <label style="min-width:190px" for="node-config-input-parallelUnitIdsAllowed"><i class="fa fa-th"></i> <span
                data-i18n="modbus-contrib.label.parallelUnitIdsAllowed"></span></label>
                <input type="checkbox" id="node-config-input-parallelUnitIdsAllowed" style="max-width:30px">
                 <span class="tooltip-text" data-i18n="modbus-contrib.label.parallelUnitIdsAllowedInfo"></span>
            </div>
            <div class="form-row">
                <label style="min-width:190px" for="node-config-input-queueLogEnabled">
                    <i class="fa fa-th"></i>
                    <span data-i18n="modbus-contrib.label.queueLogEnabled"></span>
                </label>
                <input type="checkbox" id="node-config-input-queueLogEnabled" style="max-width:30px">
            </div>
            <div class="form-row">
                <label style="min-width:190px" for="node-config-input-bufferCommands"><i class="fa fa-th"></i> <span
                data-i18n="modbus-contrib.label.queueCommands"></span></label>
                <input type="checkbox" id="node-config-input-bufferCommands" style="max-width:30px">
            </div>
            <div class="form-row">
                <label style="min-width:190px" for="node-config-input-commandDelay"><i class="icon-time"></i> <span
                data-i18n="modbus-contrib.label.commandDelay"></span></label>
                <input type="text" id="node-config-input-commandDelay" placeholder="1" style="max-width:80px">
            </div>
        </div>
        <div id="modbus-options-tab" style="display:none">
            <div class="form-row">
                <label style="min-width:190px" for="node-config-input-stateLogEnabled">
                    <i class="fa fa-th"></i>
                    <span data-i18n="modbus-contrib.label.stateLogEnabled"></span>
                </label>
                <input type="checkbox" id="node-config-input-stateLogEnabled" style="max-width:30px">
            </div>
            <div class="form-row">
                <label style="min-width:190px" for="node-config-input-failureLogEnabled">
                    <i class="fa fa-th"></i>
                    <span data-i18n="modbus-contrib.label.failureLogEnabled"></span>
                </label>
                <input type="checkbox" id="node-config-input-failureLogEnabled" style="max-width:30px">
            </div>
            <div class="form-row">
                <label style="min-width:190px" for="node-input-showErrors"><i class="fa fa-th"></i> <span
                data-i18n="modbus-contrib.label.showErrors"></span></label>
                <input type="checkbox" id="node-input-showErrors" style="max-width:30px">
            </div>
            <div class="form-row">
                <label style="min-width:190px" for="node-input-showWarnings"><i class="fa fa-th"></i> <span
                data-i18n="modbus-contrib.label.showWarnings"></span></label>
                <input type="checkbox" checked id="node-input-showWarnings" style="max-width:30px">
            </div>
            <div class="form-row">
                <label style="min-width:190px" for="node-input-showLogs"><i class="fa fa-th"></i> <span
                data-i18n="modbus-contrib.label.showLogs"></span></label>
                <input type="checkbox" checked id="node-input-showLogs" style="max-width:30px">
            </div>
        </div>
    </div>
</script>

<script type="text/x-red" data-help-name="modbus-client">
    <p><strong>
        If you have many nodes on this communication (see configuration nodes),
        think about multiple connections to your Modbus device, please!
        Many devices are able to connecting multiple.
    </strong></p>

   <p>
       Add a catch to your flow:
       <pre>
            [{"id":"cdd076d5.dab728","type":"catch","z":"b245d3e4.b52de","name":"","scope":null,"x":1020,"y":40,
            "wires":[["8516d63d.1c29a8"]]},{"id":"8516d63d.1c29a8","type":"debug","z":"b245d3e4.b52de","name":"",
            "active":true,"console":"false","complete":"true","x":1170,"y":40,"wires":[]}]
       </pre>
   </p>


    <p>Uses ModbusRTU ('modbus-serial') to read/write by ethernet host:port or TTY register/coil/input addresses.</p>
    <p>The Xstate machine ('Xstate/fsm') organizing the states to work with reconnects and a queue of commands.</p>
    <p>
        <h3>Basics</h3>
        <ul>
            <li>Type (TCP/Serial)</li>
            <li>Queue commands (true/false) - with true it buffers incoming Modbus commands and send them with
            delay</li>
            <li>Queue Work Delay (default 0 ms) - ms interval to delay sending commands from queue (sumOfWaiting=requestsPerCycle*delay)</li>
            <li>Unit-ID (default 1 [serial] or 0 [tcp]) - to set one Unit-ID for all nodes without Unit-ID.
            Set the Unit-ID of the Read/Write/Getter node's to empty for using this ID</li>
            <li>Timeout (default 1000 ms) - ms for the command timeout on ModbusRTU command</li>
            <li>Reconnect timeout (default 2000 ms) - time to wait on reconnect before next sending</li>
            <li>Reconnect on timeout - should the client do a reconnect or not on timeouts</li>
            <li>Parallel UnitId's allowed - handle commands in parallel per UnitId or not</li>
        </ul>

        <h3>TCP</h3>
        <ul>
            <li>Host - IP address</li>
            <li>Port (default 502)</li>
        </ul>

        <h3>Serial</h3>
        <ul>
            <li>Serial Port (/dev/tty.usbserial | COM[1..n])</li>
            <li>Serial Baud rate</li>
            <li>Serial Databits</li>
            <li>Serial Stopbits</li>
            <li>Serial Parity</li>
            <li>Serial Type</li>
            <li>Serial Connection delay (default 500 ms) - time to delay first command sending after reconnect</li>
            <li>ASCII start delimiter (default 0x3A, colon) - Delemiter used to identify start of slave response</li>
        </ul>
    </p>

</script>
